﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace XcoAppSpaces.Core.Ports
{
	/// <summary>
	/// Allows defining and accessing causalities, which are stored in a ThreadStatic field.
	/// From the list of active causalities, only the one that is currently on top (the last one in the list) is used,
	/// and will be returned by the "CausalityContext.Current" property.
	/// </summary>
	public class CausalityContext
	{
		[ThreadStatic]
		private static List<ICausality> _activeCausalities;

		/// <summary>
		/// Gets the currently active causality.
		/// </summary>
		public static ICausality Current
		{
			get { return (_activeCausalities == null ? null : _activeCausalities.LastOrDefault()); }
		}

		/// <summary>
		/// Gets the curently active exception port (in other words the exception port of the currently active causality).
		/// </summary>
		public static IPort<Exception> CurrentExceptionPort
		{
			get
			{
				var current = Current;
				return (current != null ? current.ExceptionPort : null);
			}
		}

		/// <summary>
		/// Gets the curently active ack port (in other words the ack port of the currently active causality).
		/// </summary>
		public static IPort<Ack> CurrentAckPort
		{
			get
			{
				var current = Current;
				return (current != null ? current.AckPort : null);
			}
		}

		/// <summary>
		/// Gets the list of all active causalities.
		/// </summary>
		public static IEnumerable<ICausality> ActiveCausalities { get { return _activeCausalities; } }

		/// <summary>
		/// Adds a causality to the list of active causalities (this will then be the "Current" causality).
		/// </summary>
		/// <param name="causality">The causality to be added.</param>
		public static void Add(ICausality causality)
		{
			if (_activeCausalities == null)
				_activeCausalities = new List<ICausality>();
			_activeCausalities.Add(causality);
		}

		/// <summary>
		/// Adds all causalities to the list of active causalities, in the given order (the last one will be the new "Current" causality).
		/// </summary>
		/// <param name="causalities">The causalities to be added.</param>
		public static void AddAll(IEnumerable<ICausality> causalities)
		{
			if (_activeCausalities == null)
				_activeCausalities = new List<ICausality>();
			_activeCausalities.AddRange(causalities);
		}

		/// <summary>
		/// Removes a specific causality from the list of active causalities.
		/// </summary>
		/// <param name="causality"></param>
		public static void Remove(ICausality causality)
		{
			if (_activeCausalities == null)
				return;
			_activeCausalities.Remove(causality);
			if (_activeCausalities.Count == 0)
				_activeCausalities = null;
		}

		/// <summary>
		/// Removes only the current causality (the one that has last been added, and is accessible by the Current property.
		/// </summary>
		public static void RemoveCurrent()
		{
			if (_activeCausalities == null || _activeCausalities.Count == 0)
				return;
			_activeCausalities.RemoveAt(_activeCausalities.Count-1);
		}
		
		/// <summary>
		/// Clears all causalities.
		/// </summary>
		public static void Clear()
		{
			_activeCausalities = null;
		}
		
		private static readonly Port<Exception> unhandledExceptionPort = new Port<Exception>();
		/// <summary>
		/// A port where exceptions are posted that were not handled by any catch statement or causality..
		/// </summary>
		public static Port<Exception> UnhandledExceptionPort
		{
			get { return unhandledExceptionPort; }
		}
	}
}
